package service

import (
	"context"
	"errors"
	"time"
	"webook/internal/domain"
	"webook/internal/events/article"
	"webook/internal/repository"
	"webook/pkg/logger"
)

//go:generate mockgen -source=./article.go -package=svcmocks -destination=./mocks/article.mock.go ArticleService
type ArticleService interface {
	Save(ctx context.Context, art domain.Article) (int64, error)
	Publish(ctx context.Context, art domain.Article) (int64, error)
	Withdraw(ctx context.Context, uid int64, id int64) error
	GetByAuthor(ctx context.Context, uid int64, offset int, limit int) ([]domain.Article, error)
	GetById(ctx context.Context, id int64) (domain.Article, error)
	GetPubById(ctx context.Context, id, uid int64) (domain.Article, error)
	ListPub(ctx context.Context, start time.Time, offset, limit int) ([]domain.Article, error)
}
type articleService struct {
	l        logger.LoggerV1
	repo     repository.ArticleRepository
	producer article.Producer
	//v1
	readerRepo repository.ArticleReaderRepository
	authorRepo repository.ArticleAuthorRepository
}

// service层同步数据
func NewArticleServiceV1(readerRepo repository.ArticleReaderRepository,
	authorRepo repository.ArticleAuthorRepository) *articleService {
	return &articleService{
		readerRepo: readerRepo,
		authorRepo: authorRepo,
		l:          logger.NewNoOpLogger(),
	}
}
func NewArticleService(repo repository.ArticleRepository, producer article.Producer) ArticleService {
	return &articleService{
		repo:     repo,
		l:        logger.NewNoOpLogger(),
		producer: producer,
	}
}
func (a *articleService) ListPub(ctx context.Context,
	start time.Time, offset, limit int) ([]domain.Article, error) {
	return a.repo.ListPub(ctx, start, offset, limit)
}

func (a *articleService) Withdraw(ctx context.Context, uid int64, id int64) error {
	return a.repo.SyncStatus(ctx, uid, id, domain.ArticleStatusPrivate)
}
func (a *articleService) Save(ctx context.Context, art domain.Article) (int64, error) {
	art.Status = domain.ArticleStatusUnPublished
	if art.Id > 0 {
		return art.Id, a.repo.Update(ctx, art)
	}
	return a.repo.Create(ctx, art)
}
func (a *articleService) Publish(ctx context.Context, art domain.Article) (int64, error) {
	art.Status = domain.ArticleStatusPublished
	return a.repo.Sync(ctx, art)
}

func (a *articleService) PublishV1(ctx context.Context, art domain.Article) (int64, error) {
	//操作制作库
	var id = art.Id
	var err error
	if art.Id > 0 {
		err = a.authorRepo.Update(ctx, art)
	} else {
		id, err = a.authorRepo.Create(ctx, art)
	}
	if err != nil {
		return 0, err
	}
	//操作线上库 id跟制作库一样
	art.Id = id
	//引入简单的重试机制
	for i := 0; i < 3; i++ {
		err = a.readerRepo.Save(ctx, art) //可能有数据 也可能没有数据 所以不用Create ，save的意思是有就更新 没有就插入
		if err == nil {
			return id, nil
		}
		a.l.Error("保存到制作库成功但是到线上库失败", logger.Int64("aid", art.Id), logger.Error(err))
	}
	a.l.Error("保存到制作库成功但是到线上库失败 重试次数耗尽", logger.Int64("aid", art.Id), logger.Error(err))
	return id, errors.New("保存到制作库成功但是到线上库失败 重试次数耗尽")
}
func (a *articleService) GetPubById(ctx context.Context, id, uid int64) (domain.Article, error) {
	res, err := a.repo.GetPubById(ctx, id)
	if err == nil {
		//发送消息
		er := a.producer.ProduceReadEvent(article.ReadEvent{
			Uid: uid,
			Aid: id,
		})
		if er != nil {
			a.l.Error("发送ReadEvent失败",
				logger.Int64("uid", uid),
				logger.Int64("aid", id),
				logger.Error(err),
			)
		}
	}

	return res, err
}

func (a *articleService) GetById(ctx context.Context, id int64) (domain.Article, error) {
	return a.repo.GetById(ctx, id)
}

func (a *articleService) GetByAuthor(ctx context.Context, uid int64, offset int, limit int) ([]domain.Article, error) {
	return a.repo.GetByAuthor(ctx, uid, offset, limit)
}
